﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace ThreeDWorld
{
    public class SkyBox : IDisposable
    {
        private VertexBuffer _VertexBuffer;
        private IndexBuffer _IndexBuffer;
        private VertexShader _VertexShader;
        private PixelShader _PixelShader;

        public SkyBox(Game game)
        {
            VertexElement[] vertexElements = new VertexElement[] { new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0) };

            Game = game;

            _VertexBuffer = new VertexBuffer(GraphicsDevice, new VertexDeclaration(vertexElements), 8, BufferUsage.WriteOnly);
            _VertexBuffer.SetData<float>(new float[] {
		        -0.5f, -0.5f, -0.5f,     0.5f, -0.5f, -0.5f,    0.5f, 0.5f, -0.5f,     -0.5f,  0.5f, -0.5f,
		        -0.5f, -0.5f,  0.5f,     0.5f, -0.5f,  0.5f,    0.5f, 0.5f,  0.5f,     -0.5f,  0.5f,  0.5f,
            });

            _IndexBuffer = new IndexBuffer(GraphicsDevice, IndexElementSize.SixteenBits, 36, BufferUsage.WriteOnly);
            _IndexBuffer.SetData<short>(new short[] {
                0, 1, 2, 2, 3, 0,
                4, 5, 6, 6, 7, 4,
                0, 4, 7, 7, 3, 0,
                1, 5, 6, 6, 2, 1,
                0, 1, 5, 5, 4, 0,
                3, 7, 6, 6, 2, 3
            });

            _VertexShader = Game.Content.Load<VertexShader>("SkyVertex");
            _PixelShader = Game.Content.Load<PixelShader>("SkyPixel");
        }

        public Game Game
        {
            get;
            private set;
        }

        public TextureCube Texture
        {
            get;
            set;
        }

        public GraphicsDevice GraphicsDevice
        {
            get { return Game.GraphicsDevice; }
        }

        public Camera Camera
        {
            get { return Game.Camera; }
        }

        public void Draw()
        {
            Matrix world = Matrix.CreateScale(128, 128, 128) * Matrix.CreateTranslation(Camera.Position);
            Matrix view = Camera.View;
            Matrix projection = Camera.Projection;

            DepthStencilState savedDepthStencilState = GraphicsDevice.DepthStencilState;
            BlendState savedBlendState = GraphicsDevice.BlendState;
            RasterizerState savedRasterizerState = GraphicsDevice.RasterizerState;

            GraphicsDevice.DepthStencilState = DepthStencilState.None;
            GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.RasterizerState = RasterizerState.CullNone;

            GraphicsDevice.SetVertexShader(_VertexShader);
            GraphicsDevice.SetPixelShader(_PixelShader);

            GraphicsDevice.SetVertexShaderConstantFloat4<Matrix>(0, ref world);
            GraphicsDevice.SetVertexShaderConstantFloat4<Matrix>(4, ref view);
            GraphicsDevice.SetVertexShaderConstantFloat4<Matrix>(8, ref projection);

            GraphicsDevice.Textures[0] = Texture;

            try
            {
                GraphicsDevice.SetVertexBuffer(_VertexBuffer);
                GraphicsDevice.Indices = _IndexBuffer;

                GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);
            }
            finally
            {
                GraphicsDevice.SetVertexBuffer(null);
                GraphicsDevice.Indices = null;

                GraphicsDevice.DepthStencilState = savedDepthStencilState;
                GraphicsDevice.BlendState = savedBlendState;
                GraphicsDevice.RasterizerState = savedRasterizerState;
            }
        }

        public void Dispose()
        {
            if (!_VertexBuffer.IsDisposed)
            {
                _VertexBuffer.Dispose();
            }

            if (!_IndexBuffer.IsDisposed)
            {
                _IndexBuffer.Dispose();
            }

            if (!_VertexShader.IsDisposed)
            {
                _VertexShader.Dispose();
            }

            if (!_PixelShader.IsDisposed)
            {
                _PixelShader.Dispose();
            }
        }
    }
}
